//
//  GSRenderedObject.cpp
//  GSEngine
//
//  Created by Александр on 02.02.14.
//  Copyright (c) 2014 SNK. All rights reserved.
//

#include "GSRenderedObject.h"
#include <GSEngine/Main/GSApplication.h>
#include <OpenGLES/ES3/gl.h>
#include <GSEngine/Resource/GSShader.h>

namespace GSEngine
{
    GSRenderedObject::GSRenderedObject():m_position(0.0f), m_scale(1.0f), m_rotation(0.0f), m_hotSpot(0.0f), m_opacity(1.0f), m_isVisible(true), m_vertexCount(0), m_type(EModelEntity)
    {
        m_worldMatrix.setIdentity();
        m_shaderProgram = new ShaderProgram();
    }
    GSRenderedObject::~GSRenderedObject()
    {
        delete m_shaderProgram;
        delete m_vertexCoords;
        delete m_texCoords;
    }
    void GSRenderedObject::render()
    {
        // get render system -> render(this)
        _RENDERSYSTEM->render(this);
    }
    // set position vec3 (x, y, z)
    void GSRenderedObject::setPosition(vec3f pos)
    {
        m_position = pos;
    }
    // set scale vec3 (scaleX, scaleY, scaleZ)
    void GSRenderedObject::setScale(vec3f scale)
    {
        m_scale = scale;
    }
    // set rotation vec3 (rotX, rotY, rotZ)
    void GSRenderedObject::setRotation(vec3f rot)
    {
        m_rotation = rot;
    }
    // set hotSpot vec3(x,y,z)
    void GSRenderedObject::setHotSpot(vec3f hotSpot)
    {
        int arraySize = 3 * m_vertexCount;
        m_hotSpot = hotSpot;
        for (int i = 0; i < arraySize; i+=3) {
            m_vertexCoords[i]   += m_hotSpot.x;
            m_vertexCoords[i+1] += m_hotSpot.y;
            m_vertexCoords[i+2] += m_hotSpot.z;
        }
    }
    // set opacity object 0..1 float
    void GSRenderedObject::setOpacity(float opacity)
    {
        m_opacity = opacity;
    }
    // set object visible true - false
    void GSRenderedObject::setVisible(bool isVisible)
    {
        m_isVisible = isVisible;
    }
    // calculating world matrix
    void GSRenderedObject::updateWorldMatrix()
    {
        // transition
        mat4 transMatrix;
        transMatrix.m41 = m_position.x;
        transMatrix.m42 = m_position.y;
        transMatrix.m43 = m_position.z;
        // scale
        mat4 scaleMatrix;
        scaleMatrix.m11 = m_scale.x;
        scaleMatrix.m22 = m_scale.y;
        scaleMatrix.m33 = m_scale.z;
        // rotate
        // rotateX
        mat4 xRotateMatrix;
        xRotateMatrix.m22 =  cosf(m_rotation.x);
        xRotateMatrix.m23 =  sinf(m_rotation.x);
        xRotateMatrix.m32 = -sinf(m_rotation.x);
        xRotateMatrix.m33 =  cosf(m_rotation.x);
        // rotateY
        mat4 yRotateMatrix;
        yRotateMatrix.m11 =  cosf(m_rotation.y);
        yRotateMatrix.m13 =  sinf(m_rotation.y);
        yRotateMatrix.m31 = -sinf(m_rotation.y);
        yRotateMatrix.m33 =  cosf(m_rotation.y);
        // rotateZ
        mat4 zRotateMatrix;
        zRotateMatrix.m11 =  cosf(m_rotation.z);
        zRotateMatrix.m12 =  sinf(m_rotation.z);
        zRotateMatrix.m21 = -sinf(m_rotation.z);
        zRotateMatrix.m22 =  cosf(m_rotation.z);
        // calculating world matrix
        m_worldMatrix = scaleMatrix * (xRotateMatrix * yRotateMatrix * zRotateMatrix) * transMatrix;
    }
    // getters
    // get position vec3 (x, y, z)
    vec3f GSRenderedObject::getPosition()
    {
        return m_position;
    }
    // get scale vec3 (scaleX, scaleY, scaleZ)
    vec3f GSRenderedObject::getScale()
    {
        return m_scale;
    }
    // get rotation vec3 (rotX, rotY, rotZ)
    vec3f GSRenderedObject::getRotation()
    {
        return m_rotation;
    }
    // set hotSpot vec3(x,y,z)
    vec3f GSRenderedObject::getHotSpot()
    {
        return m_hotSpot;
    }
    // set opacity object 0..1 float
    float GSRenderedObject::getOpacity()
    {
        return m_opacity;
    }
    // get object visibility
    bool GSRenderedObject::isVisible()
    {
        return m_isVisible;
    }
    // get worldMatrix
    mat4 GSRenderedObject::getWorldMatrix()
    {
        return m_worldMatrix;
    }
    // get shader program
    unsigned int GSRenderedObject::getShaderProgram()
    {
        return m_shaderProgram->getProgram();
    }
    void GSRenderedObject::setVertexCoords(float vertexCoords[], int count)
    {
        int arraySize = 3 * count;
        m_vertexCoords = new float[arraySize];
        memcpy(m_vertexCoords, vertexCoords, sizeof(float) * arraySize);
    }
    void GSRenderedObject::setIndices(unsigned short* indices, int arrSize)
    {
        m_indices = new unsigned short[arrSize];
        memcpy(m_indices, indices, sizeof(unsigned short) * arrSize);
    }
    void GSRenderedObject::setTextureCoords(float texCoords[], int count)
    {
        int arraySize = 2 * count;
        m_texCoords = new float[arraySize];
        memcpy(m_texCoords, texCoords, sizeof(float) * arraySize);
    }
    void GSRenderedObject::setNormalsCoords(float normCoords[], int arrSize)
    {
        m_normCoords = new float[arrSize];
        memcpy(m_normCoords, normCoords, sizeof(float) * arrSize);
    }
    void GSRenderedObject::setVertexCount(int vertexCount)
    {
        m_vertexCount = vertexCount;
    }
    void GSRenderedObject::setIndicesCount(int indicesCount)
    {
        m_indicesCount = indicesCount;
    }
    float* GSRenderedObject::getVertexCoords()
    {
        return m_vertexCoords;
    }
    unsigned short* GSRenderedObject::getIndices()
    {
        return m_indices;
    }
    float* GSRenderedObject::getTextureCoords()
    {
        return m_texCoords;
    }
    float* GSRenderedObject::getNormCoords()
    {
        return m_normCoords;
    }
    int GSRenderedObject::getVertexCount()
    {
        return m_vertexCount;
    }
    int GSRenderedObject::getIndicesCount()
    {
        return m_indicesCount;
    }
    // set texture
    void GSRenderedObject::setTexture(GSTexture* texture)
    {
        if (texture->isLoadedTexture())
        {
            glGenTextures(1, &m_texture);
            glBindTexture(GL_TEXTURE_2D, m_texture);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texture->getSize().x, texture->getSize().y, 0, GL_RGB, GL_UNSIGNED_BYTE, texture->getData());
        }
    }
    // get texture
    unsigned int GSRenderedObject::getTexture()
    {
        return m_texture;
    }
    // set shader source
    void GSRenderedObject::setShader(EShaderType type, const char* path)
    {
        std::auto_ptr<GSShader> newShader(new GSShader());
        std::string absPath = GSEngine::GSApplication::getInstance()->getPlatform()->getResourcesPath() + "/" + path;
        newShader->loadShaderSource(absPath.c_str(), "rb");
        const char* source = (const char*)newShader.get()->getData();
        m_shaderProgram->setShader(type, source);
    }
    void GSRenderedObject::setType(EEntityType type)
    {
        m_type = type;
    }
    EEntityType GSRenderedObject::getType()
    {
        return m_type;
    }
}
